<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dsp.js/0.1.0/dsp.min.js"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/dspjs@1.0.0/dsp.min.js"></script>
  <title>WebGL Water Simulation with FFT</title>
</head>
<body>
<canvas id="glCanvas" width="800" height="600"></canvas>
<script type="text/javascript">

// 顶点着色器程序
const vertexShaderSource = `
attribute vec4 a_position;
attribute float a_height;
varying vec2 v_uv;

void main() {
  vec4 position = a_position;
  position.y += a_height; // 使用从 FFT 生成的高度数据
  gl_Position = position;
  v_uv = a_position.xy;
}
`;

// 片元着色器程序
const fragmentShaderSource = `
precision mediump float;
varying vec2 v_uv;

void main() {
  vec3 color = vec3(0.0, 0.4, 0.7) + v_uv.y * 0.2;
  gl_FragColor = vec4(color, 1.0);
}
`;

// 初始化 WebGL 上下文
const canvas = document.getElementById("glCanvas");
const gl = canvas.getContext("webgl");
if (!gl) {
  console.error("WebGL not supported");
}

// 编译着色器
function compileShader(gl, source, type) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.error("Error compiling shader:", gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}

// 创建着色器程序
const vertexShader = compileShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);

const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  console.error("Error linking program:", gl.getProgramInfoLog(program));
}

// 设置 WebGL 状态
gl.useProgram(program);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// 使用 dsp.js 生成波动高度数据
function generateWaveData(numPoints, time) {
  const fft = new FFT(numPoints, 44100); // 初始化 FFT 对象
  const waveData = new Float32Array(numPoints);
  for (let i = 0; i < numPoints; i++) {
    waveData[i] = Math.sin(i * 0.1 + time) * 0.1; // 使用正弦波生成数据
  }
  fft.forward(waveData);
  return fft.spectrum.slice(0, numPoints); // 返回频谱数据的前 numPoints 个元素作为高度
}

// 顶点数据
const numPoints = 8; // 确保 numPoints 是 2 的幂
const vertices = new Float32Array([
  -0.5, -0.5,
   0.5, -0.5,
  -0.5,  0.5,
   0.5,  0.5,
   0.5,  1.0,
  -0.5,  1.0,
  -0.5,  1.5,
   0.5,  1.5,
]);

// 创建位置缓冲区
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 获取位置属性位置
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// 创建高度缓冲区
const heightBuffer = gl.createBuffer();
const heightAttributeLocation = gl.getAttribLocation(program, "a_height");
gl.enableVertexAttribArray(heightAttributeLocation);

// 动态更新波浪
let time = 0;
function render() {
  time += 0.05;

  // 生成高度数据并传递到着色器
  const heights = generateWaveData(numPoints, time);
  gl.bindBuffer(gl.ARRAY_BUFFER, heightBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(heights), gl.STATIC_DRAW);
  gl.vertexAttribPointer(heightAttributeLocation, 1, gl.FLOAT, false, 0, 0);

  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, numPoints);

  requestAnimationFrame(render);
}
render();

</script>
</body>
</html>
